package com.jiyici.data;

import java.util.ArrayList;

import jiyici.Server2ClientSyncData;
import jiyici.Server2ClientSyncItem;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.jiyici.model.Category;
import com.jiyici.model.ClientBaseModel;
import com.jiyici.model.Item;
import com.jiyici.model.ItemParam;
import com.jiyici.model.ModelUtil;
import com.jiyici.model.Record;
import com.jiyici.model.RecordParam;
import com.jiyici.network.Result;
import com.jiyici.network.WebClientFactory;
import com.jiyici.utils.LogUtil;

public class ClientSyncManager {
    private static final String TAG = "sync-manager";

    public static boolean sync() {
        LogUtil.info(TAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>sync start");
        boolean dataChanged = syncSever2Client();
        syncClient2Server();
        LogUtil.info(TAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<sync finished");
        return dataChanged;
    }

    private static final String[] SYNC_TABLES = { Category.class.getSimpleName(), Item.class.getSimpleName(),
            ItemParam.class.getSimpleName(), Record.class.getSimpleName(), RecordParam.class.getSimpleName() };

    /**
     * server -> client, algorithm:
     * 1. Get the MAX last modified time from client database
     * 2. Server send data whose last modified time is larger than client's
     * 3. Client get the updates, update local database.
     * 
     * return data changed
     */
    private static boolean syncSever2Client() {
        LogUtil.info(TAG, "begin to syncSever2Client");
        Server2ClientSyncData syncData = new Server2ClientSyncData();
        int size = SYNC_TABLES.length;
        for (int i = 0; i < size; i++) {
            String tableName = SYNC_TABLES[i];
            long lastmodified = ClientDBHelper.getInstance().dbGetMaxLastmodifiedTime(tableName);
            syncData.addSyncItem(new Server2ClientSyncItem(tableName, lastmodified));
        }
        Result result = WebClientFactory.WebClient.syncServer2Client(syncData);
        if (!result.isSuccess()) {
            //TODO error handle
            return false;
        }
        JSONObject data = result.getData();
        ArrayList<ArrayList> updateCollection = new ArrayList<ArrayList>(SYNC_TABLES.length + 1);
        int count = 0;
        try {
            JSONArray updates = data.getJSONArray("updates");
            size = updates.length();
            for (int i = 0; i < size; i++) {
                JSONObject update = updates.getJSONObject(i);
                String tableName = update.getString("tableName");
                JSONArray rows = update.getJSONArray("rows");
                int rowCount = rows.length();
                count += rowCount;
                if (rowCount == 0)
                    continue;
                ArrayList modelList = new ArrayList(rowCount + 1);
                for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
                    JSONObject rowJson = rows.getJSONObject(rowIndex);
                    ClientBaseModel model = ModelUtil.createModel(tableName);
                    if (model != null) {
                        model.fromJson(rowJson);
                        modelList.add(model);
                    }
                }
                updateCollection.add(modelList);
            }
        } catch (JSONException e) {
            LogUtil.error(TAG, "", e);
            return false;
        }
        if (count > 0) {
            ClientDBHelper.getInstance().dbServer2ClientSyncModels(updateCollection);
            return true;
        } else {
            return false;
        }
    }

    /**
     * client -> server, algorithm
     * 1. Get all dirty data
     * 2. send to server
     * 3. server hanle all the dirty datas
     * 4. server send back update info (id, lastmodified)
     * 5. client get the data, update lastmodified, clean dirty flag
     */
    private static void syncClient2Server() {
        LogUtil.info(TAG, "begin to syncClient2Server");
        JSONObject data = new JSONObject();
        JSONArray dirtyArray = new JSONArray();
        int alldirtyCount = 0;
        try {
            int size = SYNC_TABLES.length;
            for (int i = 0; i < size; i++) {
                String tableName = SYNC_TABLES[i];
                ArrayList list = ClientDBHelper.getInstance().dbQueryDirtyModels(tableName);
                int rowCount = list.size();
                alldirtyCount += rowCount;
                if (rowCount == 0)
                    continue;
                JSONObject tableJson = new JSONObject();
                tableJson.put("tableName", tableName);
                JSONArray rowArray = new JSONArray();
                for (int j = 0; j < rowCount; j++) {
                    ClientBaseModel model = (ClientBaseModel) list.get(j);
                    rowArray.put(model.toJson());
                }
                tableJson.put("rows", rowArray);
                dirtyArray.put(tableJson);
            }
            data.put("dirty", dirtyArray);
        } catch (JSONException e) {
            LogUtil.error(TAG, "", e);
            return;
        }
        
        if (alldirtyCount == 0) {
            LogUtil.info(TAG, "syncClient2Server: no dirty data");
            return;
        }
        
        Result result = WebClientFactory.WebClient.syncClient2Server(data);
        if (!result.isSuccess())//TODO error handle
            return;
        
        data = result.getData();
        ArrayList<Client2ServerSyncFeedback> feedbackList = null;
        try {
            dirtyArray = data.getJSONArray("dirty");
            int size = dirtyArray.length();
            feedbackList = new ArrayList<Client2ServerSyncFeedback>(size + 1);
            for (int i = 0; i < size; i++) {
                JSONObject dirty = dirtyArray.getJSONObject(i);
                Client2ServerSyncFeedback feedback = new Client2ServerSyncFeedback();
                feedback.fromJson(dirty);
                feedbackList.add(feedback);
            }
        } catch (JSONException e) {
            LogUtil.error(TAG, "", e);
            return;
        }
        if (feedbackList != null)
            ClientDBHelper.getInstance().dbHandleClient2ServerSyncFeedback(feedbackList);
    }

}
